home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Tool Chest / Development Platforms / MPW Related / Lurkers MPW Tool / Lurkers.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-25  |  13.2 KB  |  524 lines  |  [TEXT/MPS ]

  1. /*----------------------------------------------------------------------------------------
  2.  
  3.      Lurkers
  4.      
  5.      Greg Anderson
  6.      January 1994
  7.      
  8.      AppleLink: G.ANDERSON
  9.      Internet:  greggor@apple.com
  10.      
  11.      About Lurkers:
  12.      
  13.      Lurkers searches a directory for files that are modifiable, including files
  14.      not in a project, files checked out for modification, and modify-read-only
  15.      files.
  16.      
  17.      Search modes (pick any one):
  18.      
  19.         -modifiable            (default) Search for files not in a project, files
  20.                             checked out for modification, and modify-read-only files
  21.         -notmodifiable        Search for files checked into a project that are not
  22.                             modifiable (neither MRO nor checked out for modification)
  23.         -MRO                Search for files that are checked into a project, but have
  24.                             been modified-read-only.
  25.         -insomeproject        Search for files with a 'ckid' resource
  26.         -notinanyproject    Search for files without a 'ckid' resource
  27.     
  28.     Other flags:
  29.     
  30.         -r                    Search specified directory recursively
  31.         -s                    Short output (only list names of files that match
  32.                             the search criterion)
  33.     
  34.     Revision History:
  35.     
  36.         1.0, 19 Jan 94        Initial release
  37.         1.1, 20 Jan 94        Lurkers being too strict in its test for checked out
  38.                             files, so it missed some.  Also, I failed to check
  39.                             for errors when opening the resource fork of a file
  40.                             (oops!), so files without a resource fork reported
  41.                             the information applicable to the next item in the
  42.                             resource chain (not a problem unless you check your
  43.                             MPW shell into a project--but our team does!)
  44.         1.2, 25 Jan 94        Lurkers still missing files; make its test even less
  45.                             strict (pretty sure I got it right this time)
  46.  
  47. ----------------------------------------------------------------------------------------*/
  48.  
  49. #include    <Types.h>
  50. #include     <ctype.h>
  51. #include     <fcntl.h>
  52. #include     <string.h>
  53. #include     <stdio.h>
  54. #include    <StdLib.h>
  55. #include    <ErrMgr.h>
  56. #include    <CursorCtl.h>
  57. #include    <Errors.h>
  58. #include    <QuickDraw.h>
  59. #include    <SysEqu.h>
  60. #include    <Files.h>
  61. #include    <Memory.h>
  62. #include    <Resources.h>
  63. #include    <CursorCtl.h>
  64.  
  65. static    char*    usage = "# Usage - %s -modifiable|-notmodifiable|-MRO|-insomeproject|-notinanyproject [-s] [-r] directory\n";
  66. static    long    optionsSpecified = false;
  67.  
  68. //
  69. // We know a little bit about the contents of a ckid resource:
  70. //
  71. struct ckidheader
  72. {
  73.     long        fUnknownID;                // Different after every projector operation
  74.     long        fUnkownConstant;        // Always the same
  75.     
  76.     short        fUnknown1;                // Usually 0004
  77.     short        fIsCheckedOut;            // Usually 2, 3 or 4 if checked out, always 0 if not
  78.     
  79.     short        fIsMRO;                    // 0001 if MRO, 0000 if not 
  80.     short        fUnknown2;                // Usually 0000
  81.     
  82.     long        fUnknown3;                // I don't know or care what these are
  83.     long        fUnknown4;
  84.     long        fUnknown5;
  85.     long        fUnknown6;
  86.     long        fUnknown7;
  87.     long        fUnknown8;
  88.     
  89.     unsigned char fProjectNameLength;    // Length of project name
  90.     unsigned char fProjectName;            // null-terminated
  91.     
  92.                                         // After the project name is the name of the
  93.                                         // person who checked out this file on this
  94.                                         // machine (no relation to the person who currently
  95.                                         // has the file checked out).  Also null-terminated
  96.                                         
  97.                                         // After the user name is the revision number
  98.                                         // of the file, in ascii and null-terminated.
  99.     
  100. };
  101.  
  102. //
  103. // Projector states that we know about:
  104. //
  105. #define kNotModifiable            1
  106. #define kModifiedReadOnly        2
  107. #define kCheckedOut                4
  108. #define kNotInAProject            8
  109.  
  110. //
  111. // Global options, because there is no need to pass it around
  112. //
  113. long gOptions = 0;
  114. long gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
  115.  
  116. #define kShortOutput        1
  117. #define kRecursive            2
  118.  
  119. //
  120. // Some global variables that should be local, but I don't
  121. // want my stack to get too large, and I don't feel like
  122. // being clever.
  123. //
  124. // The code is very carefully written to avoid using these
  125. // variables after they have been reused in a recursive call.
  126. //
  127. CInfoPBRec pb;
  128. Str255 gFilename;
  129. Str255 gFolderpath;
  130.  
  131. //----------------------------------------------------------------------------------------
  132. // BuildFolderPathname: 
  133. //----------------------------------------------------------------------------------------
  134. void BuildFolderPathname(short vRefNum, long dirID, Str255 folderpath)
  135. {
  136.     Str63 thisName;
  137.     OSErr err = noErr;
  138.     
  139.     thisName[0] = 0;
  140.     
  141.     pb.dirInfo.ioCompletion            = nil;
  142.     pb.dirInfo.ioNamePtr            = thisName;
  143.     pb.dirInfo.ioResult                = noErr;
  144.     pb.dirInfo.ioVRefNum            = vRefNum;
  145.     pb.dirInfo.ioDrDirID            = dirID;
  146.     pb.dirInfo.ioFDirIndex            = -1;
  147.     
  148.     err = PBGetCatInfo(&pb,false);
  149.     
  150.     if(err == noErr)
  151.     {
  152.         BuildFolderPathname(vRefNum, pb.dirInfo.ioDrParID, folderpath);
  153.         
  154.         //
  155.         // Append 'thisName' onto folderpath
  156.         //
  157.         thisName[thisName[0] + 1] = 0;
  158.         strcpy(folderpath + folderpath[0] + 1, thisName + 1);
  159.         folderpath[0] += thisName[0];
  160.         
  161.         //
  162.         // Append a colon onto folderpath
  163.         //
  164.         folderpath[folderpath[0] + 1] = ':';
  165.         ++folderpath[0];
  166.     }
  167.     else
  168.     {
  169.         folderpath[0] = 0;
  170.     }
  171. } // BuildFolderPathname 
  172.  
  173. //----------------------------------------------------------------------------------------
  174. // ExamineProjectorInformation: 
  175. //----------------------------------------------------------------------------------------
  176. long ExamineProjectorInformation(short vRefNum, long dirID, const Str255 fileName, Str255 projectName)
  177. {
  178.     Handle ckidHandle = nil;
  179.     short resRefNum;
  180.     long projectorInfo = kNotInAProject;
  181.     
  182.     resRefNum = HOpenResFile(vRefNum, dirID, fileName, fsRdPerm);
  183.     
  184.     if(resRefNum != -1)
  185.     {
  186.         ckidHandle = Get1Resource('ckid', 128);
  187.         if(ckidHandle != nil)
  188.         {
  189.             struct ckidheader* ckpeek = nil;
  190.             
  191.             HLock(ckidHandle);
  192.             ckpeek = *( (struct ckidheader**)ckidHandle);
  193.             
  194.             //
  195.             // Look at a bit of the ckid resource
  196.             //
  197.             if(ckpeek->fIsCheckedOut != 0)
  198.                 projectorInfo = kCheckedOut;
  199.             else if((ckpeek->fIsMRO & 1) != 0)
  200.                 projectorInfo = kModifiedReadOnly;
  201.             else
  202.                 projectorInfo = kNotModifiable;
  203.             
  204.             projectName[0] = ckpeek->fProjectNameLength;
  205.             strcpy(projectName + 1, &ckpeek->fProjectName);
  206.             
  207.             HUnlock(ckidHandle);
  208.         }
  209.         
  210.         CloseResFile(resRefNum);
  211.     }
  212.     
  213.     return projectorInfo;
  214. } // ExamineProjectorInformation 
  215.  
  216. //----------------------------------------------------------------------------------------
  217. // ProcessFile: 
  218. //----------------------------------------------------------------------------------------
  219. void ProcessFile(short vRefNum, long dirID, Str255 folderpath, Str255 filename, Boolean* didOutput)
  220. {
  221.     Str255 projectName;
  222.     long fileProjectStatus = ExamineProjectorInformation(vRefNum, dirID, filename, projectName);
  223.     
  224.     //
  225.     // Ignore the file unless we want output for its state
  226.     //
  227.     if((gWantOutputForState & fileProjectStatus) != 0)
  228.     {
  229.         //
  230.         // If doing short output, only print the name of the file
  231.         //
  232.         if((gOptions & kShortOutput) != 0)
  233.         {
  234.             fprintf(stdout, "%P\n", filename);
  235.         }
  236.         else
  237.         {
  238.             //
  239.             // At this point we are commiting to printing something; if we
  240.             // have not printed anything yet, then add an extra blank line
  241.             // so things look better
  242.             //
  243.             if(*didOutput == false)
  244.             {
  245.                 fprintf(stdout, "\n");
  246.             }
  247.             
  248.             switch(fileProjectStatus)
  249.             {
  250.             case kNotInAProject:
  251.                 fprintf(stdout, "File \"%P%P\" # is not in a project\n", folderpath, filename);
  252.                 break;
  253.             
  254.             case kNotModifiable:
  255.                 fprintf(stdout, "File \"%P%P\" # is an unmodifiable copy of a file in the project %P\n", folderpath, filename, projectName);
  256.                 break;
  257.                 
  258.             case kModifiedReadOnly:
  259.                 fprintf(stdout, "File \"%P%P\" # is a modify-read-only copy of a file in the project %P\n", folderpath, filename, projectName);
  260.                 break;
  261.             
  262.             case kCheckedOut:
  263.                 fprintf(stdout, "File \"%P%P\" # is a modifiable copy of a file in the project %P\n", folderpath, filename, projectName);
  264.                 break;
  265.             }
  266.             
  267.             //
  268.             // didOutput actually means "printed a long line"; short lines don't
  269.             // count.  (This flag is used to determine if a trailing blank line
  270.             // should be added to the output)
  271.             //
  272.             *didOutput = true;
  273.         }
  274.     }
  275. } // ProcessFile 
  276.  
  277. //----------------------------------------------------------------------------------------
  278. // Lurkers: 
  279. //----------------------------------------------------------------------------------------
  280. void Lurkers(short vRefNum, long dirID)
  281. {
  282.     OSErr err = noErr;
  283.     short index = 1;
  284.     Boolean didOutput = false;
  285.  
  286.     BuildFolderPathname(vRefNum, dirID, gFolderpath);
  287.  
  288.     //
  289.     // If doing long output, tell the user what we're doing
  290.     //
  291.     if((gOptions & kShortOutput) == 0)
  292.     {
  293.         fprintf(stdout, "### Scanning folder \"%P\"\n", gFolderpath);
  294.         fflush(stdout);
  295.     }
  296.     
  297.     //
  298.     // Walk every file in this directory
  299.     //
  300.     while(err == noErr)
  301.     {
  302.         SpinCursor(1);
  303.         
  304.         gFilename[0] = 0;
  305.         
  306.         pb.hFileInfo.ioCompletion        = nil;
  307.         pb.hFileInfo.ioNamePtr            = gFilename;
  308.         pb.hFileInfo.ioResult            = noErr;
  309.         pb.hFileInfo.ioVRefNum            = vRefNum;
  310.         pb.hFileInfo.ioDirID            = dirID;
  311.         pb.hFileInfo.ioFDirIndex        = index;
  312.         
  313.         err = PBGetCatInfo(&pb,false);
  314.         
  315.         //
  316.         // We only care about files right now...
  317.         //
  318.         if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) == 0))
  319.         {
  320.             ProcessFile(vRefNum, dirID, gFolderpath, gFilename, &didOutput);
  321.         }
  322.         
  323.         ++index;
  324.     }
  325.     
  326.     //
  327.     // Add another blank line if there was any output,
  328.     // then flush stdout so that the text is actually
  329.     // printed
  330.     //
  331.     if(didOutput)
  332.     {
  333.         fprintf(stdout, "\n");
  334.     }
  335.     fflush(stdout);
  336.     
  337.     //
  338.     // Do we want to do a deep search?
  339.     //
  340.     if((gOptions & kRecursive) != 0)
  341.     {
  342.         index = 1;
  343.         err = noErr;
  344.         
  345.         //
  346.         // Walk every folder in this directory
  347.         //
  348.         while(err == noErr)
  349.         {
  350.             gFilename[0] = 0;
  351.             
  352.             pb.dirInfo.ioCompletion            = nil;
  353.             pb.dirInfo.ioNamePtr            = gFilename;
  354.             pb.dirInfo.ioResult                = noErr;
  355.             pb.dirInfo.ioVRefNum            = vRefNum;
  356.             pb.dirInfo.ioDrDirID            = dirID;
  357.             pb.dirInfo.ioFDirIndex            = index;
  358.             
  359.             err = PBGetCatInfo(&pb,false);
  360.             
  361.             //
  362.             // Call Lurkers again on every folder we find...
  363.             //
  364.             if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) != 0))
  365.             {
  366.                 Lurkers(vRefNum, pb.dirInfo.ioDrDirID);
  367.             }
  368.             
  369.             ++index;
  370.         }
  371.     }
  372. } // Lurkers 
  373.  
  374.  
  375. //----------------------------------------------------------------------------------------
  376. // main: 
  377. //----------------------------------------------------------------------------------------
  378. main( int argc, char* argv[] )
  379. {
  380.     Boolean                    optionsSpecified = false;
  381.     Boolean                    didLurkers = false;
  382.     short                    vRefNum = 0;
  383.     long                    dirID = 0;
  384.     short                    parms;
  385.     short                    status = 0;
  386.     OSErr                    err = noErr;
  387.     
  388.     InitCursorCtl(nil);
  389.     
  390.     //
  391.     // Run through the parameters once looking for things that start with "-"
  392.     //
  393.     for( parms = 1; parms < argc; parms++ )
  394.     {
  395.         short length = strlen(argv[parms]);
  396.         
  397.         //
  398.         // Look at all of the parameters that have a dash
  399.         //
  400.         if( argv[parms][0] == '-')
  401.         {
  402.             //
  403.             // Process the specific flags
  404.             //
  405.             if( strcmp( argv[parms], "-modifiable" ) == 0 )
  406.             {
  407.                 optionsSpecified = true;
  408.                 gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
  409.             }
  410.             else if( strcmp( argv[parms], "-notmodifiable" ) == 0 )
  411.             {
  412.                 optionsSpecified = true;
  413.                 gWantOutputForState = kNotModifiable;
  414.             }
  415.             else if( strcmp( argv[parms], "-MRO" ) == 0 )
  416.             {
  417.                 optionsSpecified = true;
  418.                 gWantOutputForState = kModifiedReadOnly;
  419.             }
  420.             else if( strcmp( argv[parms], "-notinanyproject" ) == 0 )
  421.             {
  422.                 optionsSpecified = true;
  423.                 gWantOutputForState = kNotInAProject;
  424.             }
  425.             else if( strcmp( argv[parms], "-insomeproject" ) == 0 )
  426.             {
  427.                 optionsSpecified = true;
  428.                 gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotModifiable;
  429.             }
  430.             else if( strcmp( argv[parms], "-r" ) == 0 )
  431.             {
  432.                 optionsSpecified = true;
  433.                 gOptions |= kRecursive;
  434.             }
  435.             else if( strcmp( argv[parms], "-s" ) == 0 )
  436.             {
  437.                 optionsSpecified = true;
  438.                 gOptions |= kShortOutput;
  439.             }
  440.             else
  441.             {
  442.                 fprintf(stderr,"### %s - \"%s\" is not an option.\n", argv[0], argv[parms]);
  443.                 status = 1;
  444.                 
  445.                 break;
  446.             }
  447.         }
  448.     }
  449.     
  450.     //
  451.     // If all of the "-" parameters were processed okay, then run through
  452.     // the parameters again looking for directory names
  453.     //
  454.     if(status == 0)
  455.     {
  456.         fprintf(stdout, "\n");
  457.         fflush(stdout);
  458.  
  459.         for( parms = 1; parms < argc; parms++ )
  460.         {            
  461.             //
  462.             // If there's no dash, then this must be the name of the directory to
  463.             // search through
  464.             //
  465.             if( argv[parms][0] != '-')
  466.             {
  467.                 short length = strlen(argv[parms]);
  468.                 FSSpec tempFSSpec;
  469.                 
  470.                 strcpy(gFilename+1, argv[parms]);
  471.                 gFilename[0] = length;
  472.                 
  473.                 //
  474.                 // Make an FSSpec so that we can get the vRefNum of the specified
  475.                 // directory.
  476.                 //
  477.                 err = FSMakeFSSpec(0, 0, gFilename, &tempFSSpec);
  478.                 if(err == noErr)
  479.                 {
  480.                     vRefNum = tempFSSpec.vRefNum;
  481.                     
  482.                     //
  483.                     // Use PBGetCatInfo to get the dirID of the specified folder
  484.                     //
  485.                     pb.dirInfo.ioCompletion            = nil;
  486.                     pb.dirInfo.ioNamePtr            = &tempFSSpec.name;
  487.                     pb.dirInfo.ioResult                = noErr;
  488.                     pb.dirInfo.ioVRefNum            = vRefNum;
  489.                     pb.dirInfo.ioDrDirID            = tempFSSpec.parID;
  490.                     pb.dirInfo.ioFDirIndex            = 0;
  491.                     
  492.                     err = PBGetCatInfo(&pb,false);
  493.                 }
  494.                 
  495.                 //
  496.                 // Run Lurkers as soon as we get a directory
  497.                 // (this is an easy and sleazy way to support multiple
  498.                 // folders on the command line)
  499.                 //
  500.                 if(err == noErr)
  501.                 {
  502.                     dirID = pb.dirInfo.ioDrDirID;
  503.                     Lurkers(vRefNum, dirID);
  504.                     didLurkers = true;
  505.                 }
  506.                 else
  507.                 {
  508.                     fprintf(stderr, "### Error %d accessing folder %P\n", err, gFilename);
  509.                 }
  510.             }
  511.         }
  512.     }
  513.     
  514.     //
  515.     // if there were errors in the parameters, print usage
  516.     //
  517.     if( (status == 1) || (didLurkers == false) )
  518.     {        
  519.         fprintf(stderr, usage, argv[0]);
  520.     }
  521.     
  522.     return status;
  523. } // main 
  524.